package com.slipper.sfts.stock.service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;

import com.slipper.common.util.BeanUtil;
import com.slipper.common.util.StringUtil;
import com.slipper.qmt.vo.DealVO;
import com.slipper.sfts.api.member.bo.MemberBrokerAccountBO;
import com.slipper.sfts.api.stock.bo.StockOrderBO;
import com.slipper.sfts.member.service.MemberBrokerAccountService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;

import com.slipper.sfts.stock.dao.StockPositionDao;
import com.slipper.sfts.api.stock.bo.StockPositionBO;

import com.slipper.common.transaction.ReadTransactional;
import com.slipper.common.transaction.WriteTransactional;
import com.slipper.common.util.Tool;

@Service
@ReadTransactional
public class StockPositionService {
    @Resource
    private StockPositionDao stockPositionDao;
    @Resource
    private MemberBrokerAccountService memberBrokerAccountService;
    
    public StockPositionBO queryBy(String id) {
        return stockPositionDao.queryBy(id);
    }
    
    public List<StockPositionBO> queryList(StockPositionBO bo) {
        return stockPositionDao.queryList(bo);
    }
    
    public PageInfo<StockPositionBO> queryPage(StockPositionBO bo) {
        PageHelper.startPage(bo.getPageNumber(), bo.getPageSize());
        return new PageInfo<>(stockPositionDao.queryList(bo));
    }
    
    @WriteTransactional
    public int save(StockPositionBO bo) {
        if (bo.getId() != null && !"".equals(bo.getId())) {
            return stockPositionDao.update(bo);
        } else {
            bo.setId(Tool.getUUID());
            return stockPositionDao.insert(bo);
        }
    }
    
    @WriteTransactional
    public int delete(StockPositionBO bo) {
        return stockPositionDao.delete(bo);
    }


    public StockPositionBO queryOneBy(String memberId, String brokerAccountId, String code) {
        return stockPositionDao.queryOneBy(memberId, brokerAccountId, code);
    }

    /**
     * 开仓
     */
    @WriteTransactional
    public void open(DealVO vo, StockOrderBO stockOrder) {
        StockPositionBO stockPosition = stockPositionDao.lockBy(
                stockOrder.getMemberId(),
                stockOrder.getBrokerAccountId(),
                stockOrder.getCode());

        if (stockPosition == null) {
            stockPosition = new StockPositionBO();
            stockPosition.setMemberId(stockOrder.getMemberId());
            stockPosition.setBrokerAccountId(stockOrder.getBrokerAccountId());
            stockPosition.setAccountId(stockOrder.getAccountId());
            stockPosition.setExchangeCode(stockOrder.getExchangeCode());
            stockPosition.setExchangeName(stockOrder.getExchangeName());
            stockPosition.setCode(stockOrder.getCode());
            stockPosition.setName(stockOrder.getName());
            stockPosition.setVolume(0);
            stockPosition.setPrice(new BigDecimal(0));
            stockPosition.setFrozenVolume(0);
            stockPosition.setAvailableVolume(0);
            stockPosition.setOpenMoney(new BigDecimal(0));
            stockPosition.setCloseMoney(new BigDecimal(0));
        }

        BigDecimal commission = calcCharge(
                stockOrder.getMemberId(),
                stockOrder.getBrokerAccountId(),
                vo.getTradedAmount());

        // 持仓均价：(持仓量 * 持仓均价 + 成交量 * 成交均价 + 成交手续费)/(持仓量 + 成交量)
        BigDecimal price = BigDecimal.valueOf(stockPosition.getVolume())
                .multiply(stockPosition.getPrice())
                .add(BigDecimal.valueOf(vo.getTradedVolume()).multiply(vo.getTradedPrice()))
                .add(commission)
                .divide(BigDecimal.valueOf(stockPosition.getVolume())
                        .add(BigDecimal.valueOf(vo.getTradedVolume())), 2, RoundingMode.HALF_UP);
        stockPosition.setPrice(price);

        stockPosition.setVolume(stockPosition.getVolume() + vo.getTradedVolume());
        stockPosition.setAvailableVolume(stockPosition.getAvailableVolume() + vo.getTradedVolume());

        // 开仓金额：成交量 * 成交均价 + 手续费 + 开仓金额
        BigDecimal openMoney = BigDecimal.valueOf(vo.getTradedVolume())
                .multiply(vo.getTradedPrice())
                .add(stockPosition.getOpenMoney())
                .add(commission)
                .setScale(2, RoundingMode.HALF_UP);
        stockPosition.setOpenMoney(openMoney);
        if (StringUtil.isBlank(stockPosition.getId())) {
            stockPosition.setId(Tool.getUUID());
            stockPositionDao.insert(stockPosition);
        } else {
            stockPositionDao.update(stockPosition);
        }
    }

    /**
     * 平仓，返回平仓前持仓信息
     */
    @WriteTransactional
    public StockPositionBO close(DealVO vo, StockOrderBO stockOrder) {
        StockPositionBO stockPosition = stockPositionDao.lockBy(
                stockOrder.getMemberId(),
                stockOrder.getBrokerAccountId(),
                stockOrder.getCode());
        if (stockPosition == null) return null;

        StockPositionBO position = BeanUtil.copy(stockPosition, StockPositionBO.class);

        // 有效数量为0，冻结量 = 成交量判定为所有持仓已卖出，删除持仓信息
        if ((stockPosition.getAvailableVolume() == 0
                    && stockPosition.getFrozenVolume().intValue() == vo.getTradedVolume().intValue())
                || (stockPosition.getVolume().intValue() == vo.getTradedVolume().intValue())) {
            stockPositionDao.delete(stockPosition);
        } else {
            BigDecimal commission = calcCharge(
                    stockOrder.getMemberId(),
                    stockOrder.getBrokerAccountId(),
                    vo.getTradedAmount());

            stockPosition.setVolume(stockPosition.getVolume() - vo.getTradedVolume());
            stockPosition.setFrozenVolume(stockPosition.getFrozenVolume() - vo.getTradedVolume());

            // 平仓金额：成交量 * 成交均价 - 手续费 + 平仓金额
            BigDecimal closeMoney = BigDecimal.valueOf(vo.getTradedVolume())
                    .multiply(vo.getTradedPrice())
                    .add(stockPosition.getCloseMoney())
                    .subtract(commission)
                    .setScale(2, RoundingMode.HALF_UP);
            stockPosition.setCloseMoney(closeMoney);
            stockPositionDao.update(stockPosition);
        }

        return position;
    }

    /**
     * 冻结持仓
     */
    @WriteTransactional
    public void freezeVolume(String memberId, String brokerAccountId, String code, int volume) {
        StockPositionBO stockPosition = stockPositionDao.lockBy(memberId, brokerAccountId, code);
        if (stockPosition != null) {
            if (stockPosition.getFrozenVolume() == null) {
                stockPosition.setFrozenVolume(volume);
            } else {
                stockPosition.setFrozenVolume(stockPosition.getFrozenVolume() + volume);
            }
            stockPosition.setAvailableVolume(stockPosition.getAvailableVolume() - volume);
            stockPositionDao.update(stockPosition);
        }
    }

    /**
     * 解冻持仓
     */
    @WriteTransactional
    public void unfreezeVolume(String memberId, String brokerAccountId, String code, int volume) {
        StockPositionBO stockPosition = stockPositionDao.lockBy(memberId, brokerAccountId, code);
        if (stockPosition != null) {
            if (stockPosition.getFrozenVolume() == null) {
                stockPosition.setFrozenVolume(volume);
            } else {
                stockPosition.setFrozenVolume(stockPosition.getFrozenVolume() - volume);
            }
            stockPosition.setAvailableVolume(stockPosition.getAvailableVolume() + volume);
            stockPositionDao.update(stockPosition);
        }
    }

    private BigDecimal calcCharge(
            String memberId,
            String brokerAccountId,
            BigDecimal money) {
        if (money == null) {
            money = new BigDecimal(0);
        }
        if (StringUtil.isBlank(memberId) || StringUtil.isBlank(brokerAccountId)) {
            return new BigDecimal(0);
        }

        MemberBrokerAccountBO account = memberBrokerAccountService.queryOneBy(memberId, brokerAccountId);
        if (account != null && account.getChargeRate() != null) {
            BigDecimal charge = money.multiply(account.getChargeRate());
            if (account.getChargeLowest() != null && charge.compareTo(account.getChargeLowest()) < 0) {
                charge = account.getChargeLowest();
            }
            return charge.setScale(2, RoundingMode.HALF_UP);
        } else if (account != null && account.getChargeLowest() != null) {
            return account.getChargeLowest().setScale(2, RoundingMode.HALF_UP);
        } else {
            return new BigDecimal(0);
        }
    }
}
